#include "stdafx.h"
#include "../Signal.h"
#include "../SigSource.h"
#include "../SigSite.h"
#include "../../ConstStr/ConstStr.h"
#include "../../UnitTest/TestMgr.h"
#include "../../UnitTest/Test.h"

using namespace Base::Sig;

namespace
{
    class KeySig : public Signal
    {
    public:
        KeySig() : m_ctrlDown(false), m_shiftDown(false), m_altDown(false)
        {}
        virtual ~KeySig()
        {}
        
    public:
        static const void* typeId()
        {
            RETURN_NEW_SIG_ID(KeySig)
        }
        
        virtual const void* id() const
        {
            RETURN_SIG_ID(KeySig)
        }
        
    public:
        bool m_ctrlDown;
        bool m_shiftDown;
        bool m_altDown;
    };
}

namespace
{
    class MouseSig : public Signal
    {
    public:
        MouseSig(): m_x(0), m_y(0)
        {}
        virtual ~MouseSig()
        {}
        
    public:
        static const void* typeId()
        {
            RETURN_NEW_SIG_ID(MouseSig)
        }
        
        virtual const void* id() const
        {
            RETURN_SIG_ID(MouseSig)
        }

    public:
        int m_x;
        int m_y;

        KeySig m_keySig;
    };
}

namespace
{
    class HelloSig : public Signal
    {
    public:
        HelloSig()
        {}
        virtual ~HelloSig()
        {}
        
    public:
        static const void* typeId()
        {
            RETURN_NEW_SIG_ID(HelloSig)
        }
        
        virtual const void* id() const
        {
            RETURN_SIG_ID(HelloSig)
        }
    };
}

namespace
{
    class Window : public SigSource
    {
    public:
        Window()
        {}
        virtual ~Window()
        {}

    public:
        void fire(const int x, 
                  const int y,
                  const bool ctrlDown,
                  const bool shiftDown,
                  const bool altDown)
        {
            MouseSig mouseSig;
            mouseSig.m_x = x;
            mouseSig.m_y = y;
            mouseSig.m_keySig.m_ctrlDown = ctrlDown;
            mouseSig.m_keySig.m_shiftDown = shiftDown;
            mouseSig.m_keySig.m_altDown = altDown;

            send(&mouseSig);
        }

        void sayHello()
        {
            HelloSig helloSig;

            send(&helloSig);
        }
    };
    
    static Window kMainWnd;
}

namespace
{
    class HUD : public SigSite
    {
    public:
        HUD()
        {}
        virtual ~HUD()
        {}
        
        void visible(const bool visible)
        {
            if (visible)
                kMainWnd.hook(this);
            else
                kMainWnd.unhook(this);
        }
        
    public:
        virtual void receive(Signal* signal)
        {
            if (signal->cast<MouseSig>())
            {
                MouseSig* mouse = static_cast<MouseSig*>(signal);
                std::cout << "Mouse Position: (" << mouse->m_x << ", " << mouse->m_y << ")" << std::endl;
            }
        }
    };
}

namespace
{
    class Command : public SigSite
    {
    public:
        Command()
        {}
        virtual ~Command()
        {}
        
    public:
        virtual void receive(Signal* signal)
        {
            if (signal->cast<KeySig>())
            {
                KeySig* key = static_cast<KeySig*>(signal);
                std::cout << "Key Ctrl is"  << (key->m_ctrlDown  ? "" : "Not") << "Down" << std::endl
                          << "Key Shift is" << (key->m_shiftDown ? "" : "Not") << "Down" << std::endl
                          << "Key Alt is"   << (key->m_altDown   ? "" : "Not") << "Down" << std::endl;
            }
        }
    };
}

namespace
{
    class OutputWnd : public SigSite
    {
    public:
        OutputWnd()
        {}
        virtual ~OutputWnd()
        {}
        
    public:
        virtual void receive(Signal* signal)
        {
            if (signal->cast<MouseSig>())
            {
                MouseSig* mouse = static_cast<MouseSig*>(signal);
                std::cout << "Mouse Position: (" << mouse->m_x << ", " << mouse->m_y << ")" << std::endl;
                std::cout << "Key Ctrl is"  << (mouse->m_keySig.m_ctrlDown  ? " " : " Not ") << "Down" << std::endl
                          << "Key Shift is" << (mouse->m_keySig.m_shiftDown ? " " : " Not ") << "Down" << std::endl
                          << "Key Alt is"   << (mouse->m_keySig.m_altDown   ? " " : " Not ") << "Down" << std::endl;
            }
            else if (signal->cast<HelloSig>())
            {
                HelloSig* mouse = static_cast<HelloSig*>(signal);
                std::cout << "Hello World!" << std::endl;
            }
        }
    };
}

void testSignal()
{
    {
        MouseSig mouse1;
        MouseSig mouse2;

        KeySig key1;
        KeySig key2;

        assert(mouse1.id() == mouse2.id());
        assert(key1.id() == key2.id());
        assert(mouse1.id() != key2.id());
    }

    {
        HUD* hud = new HUD();
        Command* cmd = new Command();
        OutputWnd* outputWnd = new OutputWnd();

        kMainWnd.hook(hud);
        kMainWnd.hook(cmd);
        kMainWnd.hook(outputWnd);

        kMainWnd.fire(1, 1, false, true, false);
        kMainWnd.fire(1, 2, true, true, false);
        kMainWnd.fire(200, 200, true, false, false);

        kMainWnd.sayHello();

        Base::Sig::SigSite* temp = 0;

        temp = hud;
        delete hud;
        assert(!kMainWnd.hookedWith(temp));

        temp = cmd;
        delete cmd;
        assert(!kMainWnd.hookedWith(temp));

        temp = outputWnd;
        delete outputWnd;
        assert(!kMainWnd.hookedWith(temp));
    }

    {
        Window* childWnd = new Window();

        HUD* hud = new HUD();
        Command* cmd = new Command();
        OutputWnd* outputWnd = new OutputWnd();

        childWnd->hook(hud);
        assert(hud->hooked());

        childWnd->hook(cmd);
        assert(cmd->hooked());

        childWnd->hook(outputWnd);
        assert(outputWnd->hooked());

        delete childWnd;

        assert(!hud->hooked());
        assert(!cmd->hooked());
        assert(!outputWnd->hooked());

        delete hud;
        delete cmd;
        delete outputWnd;
    }
}

static Base::UnitTest::Test kTestSignal(testSignal);